package uk.co.roguelj.btwave;

import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

	BluetoothAdapter mBluetoothAdapter;
	Context context;
	Thread workerThread;
	Boolean stopWorker = false;
	SharedPreferences prefs;
	
	boolean inPacket = false;
	boolean freshPacket = false;
	boolean hasPower = false;
	int latestByte;
	int lastByte;
	int packetIndex = 0;
	int packetLength = 0;
	int checksum = 0;
	int checksumAccumulator = 0;
	int eegPowerLength = 0;
	int signalQuality = 200;
	int attention = 0;
	int meditation = 0;
	int MAX_PACKET_LENGTH = 32;
	int EEG_POWER_BANDS = 8;	
	int[] packetData = new int[MAX_PACKET_LENGTH];
	long[] eegPower = new long[EEG_POWER_BANDS];

	TextView tvSQ; 
	TextView tvA; 
	TextView tvM; 
	TextView tvD; 
	TextView tvT; 
	TextView tvLA; 
	TextView tvHA; 
	TextView tvLB; 
	TextView tvHB; 
	TextView tvLG; 
	TextView tvMG; 
	 
	
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        context = getApplicationContext();
        prefs = getPreferences(MODE_PRIVATE);
        clearEegPower();
 
		tvSQ = (TextView) findViewById(R.id.signalQualityValueTextBox); 
		tvA = (TextView) findViewById(R.id.attentionValueTextBox); 
		tvM = (TextView) findViewById(R.id.meditationValueTextBox); 
		tvD = (TextView) findViewById(R.id.deltaValueTextBox); 
		tvT = (TextView) findViewById(R.id.thetaValueTextBox); 
		tvLA = (TextView) findViewById(R.id.lowAlphaValueTextBox); 
		tvHA = (TextView) findViewById(R.id.highAlphaValueTextBox); 
		tvLB = (TextView) findViewById(R.id.lowBetaValueTextBox); 
		tvHB = (TextView) findViewById(R.id.highBetaValueTextBox); 
		tvLG = (TextView) findViewById(R.id.lowGammaValueTextBox); 
		tvMG = (TextView) findViewById(R.id.midGammaValueTextBox); 
		  	  
        setContentView(R.layout.activity_main);    
    } // end onCreate
  
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
    	try{
            getMenuInflater().inflate(R.menu.main, menu);
            return true;
    	} catch (Exception e) {
			Toast toast = Toast.makeText(context, e.getMessage() , Toast.LENGTH_SHORT);
            toast.show();	
            return false;
    	}

    }

    public boolean showSettings(MenuItem item) { 
    	try{
        	Intent intent = new Intent(this,PrefsActivity.class);
        	startActivity(intent);
        	return true;
    	} catch (Exception e) {
			Toast toast = Toast.makeText(context, e.getMessage() , Toast.LENGTH_SHORT);
            toast.show();	
            return false;
    	}

    }
   
    public void connect(View v) {
    	
    	String macAddress = prefs.getString("PairedDeviceMacAddress", "NOTHING");
    	if(macAddress=="NOTHING"){
			Toast toast = Toast.makeText(context, "No bluetooth device set - check Settings." , Toast.LENGTH_SHORT);
            toast.show();	
    		return;
    	} // End Mac Address Check
    	
    	try {
    		
    		UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); //Standard SerialPortService ID
    		BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(macAddress); 	
			BluetoothSocket mmSocket = device.createRfcommSocketToServiceRecord(uuid);
			
		    mmSocket.connect();
		    final InputStream mmInputStream = mmSocket.getInputStream();

		    final Handler handler = new Handler();
	        workerThread = new Thread(new Runnable() {
	            public void run() {
	            	while(!Thread.currentThread().isInterrupted() && !stopWorker) {
	            		int bytesAvailable;
	            		
	            		try {
	            			bytesAvailable = mmInputStream.available();
						
	            			if(bytesAvailable > 0) {
							
	                           byte[] packetBytes = new byte[bytesAvailable];
	                           mmInputStream.read(packetBytes);
	                        	
	                           int i;
	                           for(i=0; i<bytesAvailable; i++){
	                        	   latestByte = packetBytes[i];
	                        		
	                        	   if (latestByte <0) latestByte +=256;
	                        
			                		// Build a packet if we know we're and not just listening for sync bytes.
			                		if (inPacket) {
			                		
			                			// First byte after the sync bytes is the length of the upcoming packet.
			                			if (packetIndex == 0) {
			                				packetLength = latestByte;
		
			                				// Catch error if packet is too long
			                				if (packetLength > MAX_PACKET_LENGTH) {
			                					Log.w("BTWave", "ERROR: Packet too long");
			                					inPacket = false;
			                				} // End packet length check
			                				
			                			} else if (packetIndex <= packetLength) {
			                				
			                				// Run of the mill data bytes
			                				packetData[packetIndex - 1] = latestByte;	// Store the byte in an array for parsing later.   				
			                				checksumAccumulator += latestByte;			// Keep building the checksum.
			                				
			                			} else if (packetIndex > packetLength) {
			                				// We're at the end of the data payload.
			                				
			                				// Check the checksum.
			                				checksum = latestByte;
			                				checksumAccumulator = (byte) (255 - checksumAccumulator);
		
			                				// Do they match?
			                				if (checksum == checksumAccumulator) {
			                					Log.w("BTWave","Checksum OK");
		
			                					// Parse the data. parsePacker() returns true if parsing succeeds.
			                					if (parsePacket()) {
			                						freshPacket = true;
			                					} else {
			                						// Parsing failed, send an error.
			                						Log.w("BTWave", "ERROR: Could not parse");
			                					} // end parse packet check
			                					
			                				} else {
			                					Log.w("BTWave", "ERROR: Checksum Mismatch");
			                				}
			                				
			                				inPacket = false;
			                			}
			                			
			                			packetIndex++;
			                		}
			                		
			                		// Look for the start of the packet
			                		if ((latestByte == 170) && (lastByte == 170) && !inPacket) {
			                			inPacket = true;
			                			packetIndex = 0;
			                			packetLength = 0; // Technically not necessarry.
			                			checksum = 0; // Technically not necessary.
			                			checksumAccumulator = 0;
			                			//clearPacket(); // Zeros the packet array, technically not necessarry.
			                			//clearEegPower(); // Zeros the EEG power. Necessary if hasPower turns false... better off on the gettter end?	 
			                		}
			                		
			                		// Keep track of the last byte so we can find the sync byte pairs.
			                		lastByte = latestByte;
			                	
			                		if(freshPacket) freshPacket = false;
	                			                		
	                		
	                        	} // End bytes for loop
	                         
	                           // if we have power, then update the UI
	                        	if(hasPower){
	                        		  handler.post(new Runnable(){
	                                      public void run(){
	                                    	                                      	  
	                                          tvSQ.setText(String.valueOf(signalQuality));
	                                          tvA.setText(String.valueOf(attention));
	                                          tvM.setText(String.valueOf(meditation));
	                                          tvD.setText(String.valueOf(eegPower[0]));
	                                          tvT.setText(String.valueOf(eegPower[1]));
	                                          tvLA.setText(String.valueOf(eegPower[2]));
	                                          tvHA.setText(String.valueOf(eegPower[3]));
	                                          tvLB.setText(String.valueOf(eegPower[4]));
	                                          tvHB.setText(String.valueOf(eegPower[5]));
	                                          tvLG.setText(String.valueOf(eegPower[6]));
	                                          tvMG.setText(String.valueOf(eegPower[7]));
	                                          
	                                      } // end run
	                                  });
	                        	} // End if hasPower check                         
	                           
	                       } // End bytesAvailable check
						
					} catch (IOException e) {
						oplog(e.getMessage(), true);
						
					} // End try
                       
	               } // End while
	            	
	            } // End run
	        });
	        workerThread.start();
	        
		       
		} catch (IOException e) {
			oplog(e.getMessage(), true);
            
		} catch (Exception e) {
			oplog(e.getMessage(), true);
        
		} // End try
    	       
    } // End connect
    
    // Loop through the packet, extracting data.
    boolean parsePacket() {
    	
    	// Based on mindset_communications_protocol.pdf from the Neurosky Mindset SDK.
    	hasPower = false;
    	clearEegPower();	// clear the eeg power to make sure we're honest about missing values... null would be better than 0.
    	
    	for (byte i = 0; i < packetLength; i++) {
    		switch (packetData[i]) {
    			case 2:
    				signalQuality = packetData[++i];
    				break;
    			case 4:
    				attention = packetData[++i];
    				break;
    			case 5:
    				meditation = packetData[++i];
    				break;
    			case 131:
    				// ASIC_EEG_POWER: eight big-endian 3-byte unsigned integer values representing delta, theta, low-alpha high-alpha, low-beta, high-beta, low-gamma, and mid-gamma EEG band power values			 
    				// The next byte sets the length, usually 24 (Eight 24-bit numbers... big endian?)
    				eegPowerLength = packetData[++i];

    				// Extract the values. Possible memory savings here by creating three temp longs?
    				for(int j = 0; j < EEG_POWER_BANDS; j++) {
    					eegPower[j] = (packetData[++i] << 16) | (packetData[++i] << 8) | packetData[++i];
    				}

    				hasPower = true;

    				break;
    			default:
    				return false;
    		}
    	}
    	return true;
    } // End parsePacket
    
    // Zero all the values of the packet array
    void clearPacket() {
    	for (byte i = 0; i < MAX_PACKET_LENGTH; i++) {
    		packetData[i] = 0;
    	}	 
    } // End clearPacket

    // Zero the power bands.
    void clearEegPower() {
    	for(byte i = 0; i < EEG_POWER_BANDS; i++) {
    		eegPower[i] = 0;
    	}
    } // End clearEegPower

    public void oplog(String msg, Boolean UI) {
    	Log.w("BTWave",msg);
    	
    	if (UI) {
			Toast toast = Toast.makeText(context, msg , Toast.LENGTH_SHORT);
	        toast.show();   	
    	} // End UI Check
    }
    
} // End class



